﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  VoicemailReminder.cs
//    
// Abstract:
//
//  Workflow application for controlling the callflow of a
//  reminder for a voicemail.
//  
//--

using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;
using System.Workflow.Activities;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.Rtc.Workflow.Activities;
using Microsoft.Speech.Synthesis;
using Microsoft.Speech.Recognition;
using Microsoft.Rtc.Collaboration.AudioVideo;
using Microsoft.Rtc.Collaboration;
using System.Threading;

namespace Microsoft.EnhancedReminders.Dispatcher.Workflow
{
    /// <summary>
    /// Workflow class for controlling the call flow of voicemail reminders
    /// </summary>
    public sealed partial class VoicemailReminder : SequentialWorkflowActivity
    {
        private Player _player;
        private WmaFileSource _source;
        private AudioVideoCall _call;
        private VoicemailNotificationItem _message;
        private NotificationResult _result;
        private AutoResetEvent _voiceMailFinishedEvent = new AutoResetEvent(false);

        /// <summary>
        /// Get or set the NotificationItem to be delivered. This is intended
        /// to be used as an input parameter.
        /// </summary>
        public VoicemailNotificationItem Message
        {
            get
            {
                return _message;
            }
            set
            {
                _message = value;
                getPin.Notification = value;
                _result = new NotificationResult(_message, NotificationResultStatus.Error,
                    NotificationChannelType.None);
            }
        }

        /// <summary>
        /// Get or set the path to the temporary file name to use for playing a voicemail notification
        /// </summary>
        public string TemporaryVoicemailAudioPath
        {
            get;
            set;
        }

        /// <summary>
        /// Get or set the NotificationResult from the attempt to deliver
        /// the notification item. This is intended to be accessed as a
        /// return value.
        /// </summary>
        public NotificationResult MessageResult
        {
            get
            {
                return _result;
            }
            set
            {
                _result = value;
            }
        }

        /// <summary>
        /// ctor.
        /// </summary>
        public VoicemailReminder()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Returns the base path to the Dispatcher assemblies.
        /// </summary>
        public string BasePath
        {
            get
            {
                return System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location) + "\\";
            }
        }

        #region Event Handlers

        private void player_StateChanged(object sender, PlayerStateChangedEventArgs e)
        {
            try
            {
                //if player has stopped playing then close the source and detach the flow
                if (e.State == PlayerState.Stopped)
                {

                    _call = callProviderActivity1.CallProvider.Call as AudioVideoCall;
                    _source.Close();
                    _player.DetachFlow(_call.Flow);
                }
            }
            catch (ArgumentNullException argNullEx)
            {
                Logger.WriteTraceError(argNullEx, "Error while trying to detach flow from player.");
            }
            finally
            {
                _voiceMailFinishedEvent.Set();
            }
        }

        /// <summary>
        /// This method attaches UCMA player object to the flow of the existing call and starts playing wma file
        /// </summary>
        private void PlayVoiceMailFile()
        {
            try
            {
                //Get handle of the Current call
                _call = callProviderActivity1.CallProvider.Call as AudioVideoCall;

                //instantiate ucma player and attach it to the flow of the call
                _player = new Player();
                _player.StateChanged += new EventHandler<PlayerStateChangedEventArgs>(player_StateChanged);
                if (_player.AudioVideoFlows.Count <= 0)
                    _player.AttachFlow(_call.Flow);

                //setup the wma source
                _source = new WmaFileSource(TemporaryVoicemailAudioPath);
                _source.EndPrepareSource(_source.BeginPrepareSource(MediaSourceOpenMode.Buffered, null, null));
                _player.SetMode(PlayerMode.Automatic);
                _player.SetSource(_source);

                //start playing voicemail
                _player.Start();
            }
            catch (ArgumentNullException argNullEx)
            {
                Logger.WriteTraceError(argNullEx, "Player initialization failed.");
            }
            catch (Microsoft.Rtc.Signaling.OperationFailureException opFailEx)
            {
                Logger.WriteTraceError(opFailEx, "Player cannot be started.");
            }
            catch (InvalidOperationException ioEx)
            {
                Logger.WriteTraceError(ioEx, "Setting up source failed.");
            }
        }

        private void VoicemailFrom_TurnStarting(object sender, SpeechTurnStartingEventArgs e)
        {
            //Plays the begining of voice mail reminder
            speakVoicemailFrom.MainPrompt.ClearContent();
            if (_message.VoicemailCallerId != null)
            {
                speakVoicemailFrom.MainPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_MainPrompt_1_Ani); // "You have a new voicemail from 

                if (_message.VoicemailCallerId.Length > 1 && _message.VoicemailCallerId.StartsWith("+", StringComparison.OrdinalIgnoreCase))
                {
                    speakVoicemailFrom.MainPrompt.AppendSsmlMarkup(string.Format(CultureInfo.InvariantCulture, "<say-as type=\"telephone\">{0}</say-as>", _message.VoicemailCallerId.Substring(1, _message.VoicemailCallerId.Length - 1)));
                }
                else
                {
                    speakVoicemailFrom.MainPrompt.AppendText(_message.VoicemailCallerId);
                }
            }
            else
            {
                speakVoicemailFrom.MainPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_MainPrompt_1_NoAni); // "You have a new voicemail from 
            }
            speakVoicemailFrom.MainPrompt.AppendBreak(TimeSpan.FromMilliseconds(250.0));
        }

        private void PlayVoiceMail(object sender, EventArgs e)
        {
            //play actual voice mail
            Logger.WriteTraceInformation(Message.Context, "Workflow: Outputting voicemail.");
            PlayVoiceMailFile();
            _voiceMailFinishedEvent.WaitOne();

        }

        private void AskVoicemailTurnStarting(object sender, SpeechTurnStartingEventArgs e)
        {
            Logger.WriteTraceInformation(Message.Context, "Workflow: Outputting voicemail.");
            Debug.Assert(_message.VoicemailAudio != null);

            // Only set the prompts and grammars if no grammars have been set yet; TurnStarting is called
            // every time the prompt speaks and we don't need to reset these fields every time.
            if (askVoicemail.Grammars.Count == 0)
            {
                askVoicemail.MainPrompt.ClearContent();

                askVoicemail.MainPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_MainPrompt_2); // "Would you like to repeat or dismiss this reminder?"

                askVoicemail.Prompts.HelpPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_HelpPrompt_1); // "To hear the voicemail again, say REPEAT, or press 1
                askVoicemail.Prompts.HelpPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_HelpPrompt_2); // "To dismiss it, say DISMISS, or press 7.  

                askVoicemail.Prompts.RepeatPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_RepeatPrompt); // "OK here it is again"
                askVoicemail.Prompts.RepeatPrompt.AppendPromptBuilder(askVoicemail.MainPrompt);

                askVoicemail.Prompts.SilencePrompt.AppendAudio(BasePath + PromptAudio.Voicemail_SilencePrompt); // "To hear the voicemail again, say REPEAT, or press 1

                askVoicemail.Prompts.EscalatedSilencePrompt.AppendAudio(BasePath + PromptAudio.Voicemail_EscalatedSilencePrompt_1); // "To hear the voicemail again, say REPEAT, or press 1
                askVoicemail.Prompts.EscalatedSilencePrompt.AppendAudio(BasePath + PromptAudio.Voicemail_EscalatedSilencePrompt_2); // "To dismiss it, say DISMISS, or press 7.  

                askVoicemail.Prompts.NoRecognitionPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_NoRecognitionPrompt); // "Sorry, I missed that."

                askVoicemail.Prompts.EscalatedNoRecognitionPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_EscalatedNoRecognitionPrompt_1); // "I didn’t get that either.  To hear the voicemail again, say REPEAT, or press 1.  
                askVoicemail.Prompts.EscalatedNoRecognitionPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_EscalatedNoRecognitionPrompt_2); // "To dismiss it, say DISMISS, or press 7.  

                askVoicemail.DtmfGrammars.Add(new Grammar(BasePath + Helpers.GetSetting<string>("DtmfGrammar", null), "Voicemail"));
                askVoicemail.Grammars.Add(new Grammar(BasePath + Helpers.GetSetting<string>("VoiceGrammar", null), "Voicemail"));

                askVoicemailHelp.Grammar = new Grammar(BasePath + Helpers.GetSetting<string>("VoiceGrammar", null), "VoicemailHelp");
                askVoicemail.Grammars.Add(new Grammar(BasePath + Helpers.GetSetting<string>("VoiceGrammar", null), "VoicemailRepeat"));
                askVoicemail.DtmfGrammars.Add(new Grammar(BasePath + Helpers.GetSetting<string>("DtmfGrammar", null), "VoicemailRepeat"));
            }
        }

        /// <summary>
        /// Called when the user says "dismiss." Sets the audio for the dismiss prompt.
        /// </summary>
        private void DismissTurnStarting(object sender, SpeechTurnStartingEventArgs e)
        {
            Logger.WriteTraceInformation(Message.Context, "Workflow: User has dismissed the notification.");
            dismiss.MainPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_Dismiss); // "OK. I've dismissed this reminder."
            dismiss.MainPrompt.AppendAudio(BasePath + PromptAudio.Generic_Goodbye);
        }

        /// <summary>
        /// Called when the user has exceeded the number of no recognition events for the askVoicemail QA.
        /// Sets the audio for the goodbye prompt.
        /// </summary>
        private void AskVoicemailGoodbyeNoRecoTurnStarting(object sender, SpeechTurnStartingEventArgs e)
        {
            Logger.WriteTraceInformation(Message.Context, "Workflow: User has exceeded maximum number of non-recognitions. Disconnecting call.");
            askVoicemailGoodbyeNoReco.MainPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_GoodbyeNoReco);
        }

        /// <summary>
        /// Called when the user has been silent for the maximum number of turns at the askVoicemail QA.
        /// Sets the audio for the goodbye prompt.
        /// </summary>
        private void AskVoicemailGoodbyeSilenceTurnStarting(object sender, SpeechTurnStartingEventArgs e)
        {
            Logger.WriteTraceInformation(Message.Context, "Workflow: User has exceeded maximum number of silences. Disconnecting call.");
            askVoicemailGoodbyeSilence.MainPrompt.AppendAudio(BasePath + PromptAudio.Voicemail_GoodbyeSilence);
        }
        #endregion Event Handlers

        #region Code Conditions
        private void IsDismiss(object sender, ConditionalEventArgs e)
        {
            e.Result = askVoicemail.RecognitionResult.Semantics.Value.ToString() == "Dismiss";
        }
        private void IsRepeat(object sender, ConditionalEventArgs e)
        {
            e.Result = askVoicemail.RecognitionResult.Semantics.Value.ToString() == "Repeat";
        }

        #endregion Code Conditions

        /// <summary>
        /// Returns a string containing short versions of voicemail options intended to be
        /// inserted in the middle of a prompt. For example: "REPEAT or DISMISS".
        /// </summary>
        private static string GetVoicemailOptions()
        {
            StringBuilder prompts = new StringBuilder();

            prompts.Append(string.Format(CultureInfo.CurrentUICulture, PromptResources.VoicemailOptions_Split, PromptResources.VoicemailOptions_Repeat));
            prompts.Append(string.Format(CultureInfo.CurrentUICulture, PromptResources.VoicemailOptions_Final, PromptResources.VoicemailOptions_Dismiss));

            return prompts.ToString();
        }

        /// <summary>
        /// Returns a PromptBuilder containing longer help versions of voicemail options
        /// with a short break between each option. It is assumed that this will be appended
        /// to the beginning or end of an existing prompt, rather than placed in the middle
        /// of a short phrase as with options.
        /// </summary>
        private static PromptBuilder GetVoicemailHelp()
        {
            PromptBuilder prompts = new PromptBuilder(CultureInfo.CurrentUICulture);

            prompts.AppendText(PromptResources.VoicemailHelp_Repeat);
            prompts.AppendBreak(TimeSpan.FromMilliseconds(250.0));
            prompts.AppendText(PromptResources.VoicemailHelp_Dismiss);
            prompts.AppendBreak(TimeSpan.FromMilliseconds(250.0));

            return prompts;
        }

        /// <summary>
        /// This should be called after we've verified the user's PIN. Verification
        /// of a PIN means that this notification was successful.
        /// </summary>
        private void SetNotificationResult(object sender, EventArgs e)
        {
            _result.Status = NotificationResultStatus.Success;
        }




    }
}
